﻿using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Syspetro.Core.Extensions
{
    /// <summary>
    /// 合并表达式 And Or  Not扩展
    /// </summary>
    public static class ExpressionHelper
    {
        /// <summary>
        /// 合并表达式 expr1 AND expr2
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr1"></param>
        /// <param name="expr2"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            if (expr1 == null)
                return expr2;
            else if (expr2 == null)
                return expr1;
            ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
            MyExpressionVisitor visitor = new(newParameter);
            var left = visitor.Visit(expr1.Body);
            var right = visitor.Visit(expr2.Body);

            return Expression.Lambda<Func<T, bool>>(Expression.And(left, right), newParameter);
        }
        /// <summary>
        /// 合并表达式 expr1 or expr2
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr1"></param>
        /// <param name="expr2"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
        {
            if (expr1 == null)
                return expr2;
            else if (expr2 == null)
                return expr1;
            ParameterExpression newParameter = Expression.Parameter(typeof(T), "c");
            MyExpressionVisitor visitor = new(newParameter);

            var left = visitor.Visit(expr1.Body);
            var right = visitor.Visit(expr2.Body);
            var body = Expression.Or(left, right);
            return Expression.Lambda<Func<T, bool>>(body, newParameter);
        }
        /// <summary>
        /// 取反表达式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expr)
        {
            if (expr == null) return null;
            var candidateExpr = expr.Parameters[0];
            var body = Expression.Not(expr.Body);

            return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
        }
        /// <summary>
        /// 获取表达式属性列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static List<MemberInfo> SelectMembers<T>(this Expression<Func<T, object>> expr)
        {
            return SelectMembers(expr.Body);
        }
        private static List<MemberInfo> SelectMembers(Expression properties)
        {
            if( properties.NodeType == ExpressionType.Convert)
            {
                List<MemberInfo> newArrMembers = new()
                {
                    ((MemberExpression)((UnaryExpression)properties).Operand).Member
                };
                return newArrMembers;
            }
            else
            {
                var newExp = (NewExpression)properties;
                if (newExp != null)
                {
                    return newExp.Members.ToList();
                }
                else
                {
                    var newArr = (NewArrayExpression)properties;
                    if (newArr != null)
                    {
                        List<MemberInfo> newArrMembers = new();
                        foreach (var newArrExp in newArr.Expressions)
                            newArrMembers.AddRange(SelectMembers(newArrExp));
                        return newArrMembers.Distinct().ToList();
                    }
                    return null;
                }
            }
        }
    }
}
